import {MethodUndefinedException} from "./exception";

/**
 * 返回一个空的对象，出于对象保护，每次都返回一个空的对象
 *
 * @returns 空的对象
 */
const object = (): {} => {
    return {}
}

/**
 * 返回一个空的数组，出于对象保护，每次都返回一个空的数组
 *
 * @returns 空数组
 */
const array = (): [] => {
    return []
}

/**
 * 一个空的函数
 *
 * 提供默认的函数，减少代码中可能出现的非空判断
 */
const empty_method = (): void => {
}


/**
 * 判断对象是否为空
 *
 * ES5+ 环境下，undefined 为只读属性，null 为赋值属性，后续代码需遵守此规范
 *
 * @param obj 需要判断的值
 * @returns 布尔值
 */
const isBlank = (obj: unknown): boolean => {
    return obj == null;
};

/**
 * 判断对象是否非空
 *
 * @param obj 需要判断的值
 * @returns 布尔值
 */
const isNotBlank = (obj: unknown): boolean => {
    return obj != null;
};

/**
 * 判断字符串是否有内容（去除空白后）
 *
 * @param str 需要判断的值
 * @returns 布尔值
 */
const hasText = (str: unknown): boolean => {
    if (isEmpty(str)) {
        return false; // 修正：原代码逻辑反了
    }
    return typeof str === 'string' && str.trim().length > 0;
};

/**
 * 判断对象是否为空
 *
 * @param obj 需要判断的值
 * @returns 布尔值
 */
const isEmpty = (obj: unknown): boolean => {
    if (obj == null) {
        return true;
    }
    // 检查是否有 length 属性且为 0（适用于字符串、数组等）
    return obj.length === 0;
};

/**
 * 判断对象是否非空
 *
 * @param obj 需要判断的值
 * @returns 布尔值
 */
const isNotEmpty = (obj: unknown): boolean => {
    return !isEmpty(obj);
};

/**
 * 如果为空则给出默认值
 *
 * @param obj 需要判断的值
 * @param def 默认值
 * @returns 非空值或默认值
 */
const nvl = <T>(obj: T | undefined | null, def: T): T => {
    return obj == null ? def : obj;
};

/**
 * 转换为布尔类型
 *
 * @param val 任意类型的值
 * @returns 转换后的布尔值
 * @throws {TypeError} 当无法转换时抛出错误
 */
const toBoolean = (val: unknown): boolean => {
    if (typeof val === 'boolean') {
        return val;
    } else if (typeof val === 'number') {
        return val !== 0;
    } else if (typeof val === 'string') {
        const lowerVal = val.toLowerCase();
        return lowerVal === '1' || lowerVal === 'y' || lowerVal === 'true';
    }
    throw new TypeError(`Cannot convert to boolean: ${String(val)}`);
};

/**
 * 转换为字符串类型
 *
 * @param val 任意类型的值
 * @returns 转换后的字符串
 */
const toString = (val: unknown): string => {
    if (typeof val === 'string') {
        return val;
    } else if (Array.isArray(val)) {
        return val.join(',');
    } else if (val === null || val === undefined) {
        return '';
    }
    return String(val);
};

/**
 * 转换为数字类型
 *
 * @param val 任意类型的值
 * @returns 转换后的数字
 * @throws {TypeError} 当无法转换时抛出错误
 */
const toNumber = (val: unknown): number => {
    if (typeof val === 'number') {
        return val;
    }
    const num = Number(val);
    if (isNaN(num)) {
        throw new TypeError(`Cannot convert to number: ${String(val)}`);
    }
    return num;
};

/**
 * 转换为数组类型
 *
 * @param val 任意类型的值
 * @returns 转换后的数组（如果输入已经是数组则返回原数组）
 * @throws {TypeError} 当输入不是数组时抛出错误
 */
const toArray = <T>(val: unknown): T[] => {
    if (Array.isArray(val)) {
        return val as T[];
    } else if (typeof val === 'string') {
        return val.split(',');
    }
    throw new TypeError(`Cannot convert to array: ${String(val)}`);
};

/**
 * 判断对象是否为空，是的话抛出异常
 *
 * 仿 Promise 函数
 *
 * @param obj 需要判断的值
 * @returns 包含then方法的对象
 */
const ifNotBlank = <T>(obj?: T): Promise<T> => {
    if (obj == null) {
        return Promise.resolve(obj);
    } else {
        return Promise.reject();
    }
};

/**
 * 判断对象是否为空，是的话抛出异常
 *
 * 仿 Promise 函数
 *
 * @param obj 需要判断的值
 * @returns 包含then方法的对象
 */
const ifNotEmpty = <T>(obj?: T): Promise<T> => {
    if (isEmpty(obj)) {
        return Promise.resolve(obj);
    } else {
        return Promise.reject();
    }
};

/**
 * 复制所有字段到目标对象。
 *
 * 目标对象与源对象完全一致。
 *
 * 使用场景：
 * - 在 vue 环境下，有时候为了避免侦听，需要采用这种方式复制字段值；
 *
 * @param from - 原始对象
 * @param to - 目标对象
 */
const clone = (from: any, to: any): void => {
    // 复制所有字段到目标对象
    for (let key in from) {
        if (from.hasOwnProperty(key)) {
            to[key] = from[key];
        }
    }

    // 清除多余的字段
    for (let key in to) {
        if (!from.hasOwnProperty(key)) {
            // 在 vue 环境下，用 delete 不见得会好
            to[key] = null;
        }
    }
}

/**
 * 覆盖所有的字段到目标对象
 *
 * 常用语构筑基础配置，当用户制定了新的配置，这时候合并双方的配置信息。
 *
 * 当前函数的特点是：可以置空一些默认配置，
 *
 * 如果源对象字段值为 undefined，合并之后，目标对象该字段也会是 undefined。
 *
 * 注意：与这些函数的行为不同：
 * - Object.assign(from, to)
 * - ES6 展开运算符 ...
 *
 * @param from - 原始对象
 * @param to - 目标对象
 */
const replace = (from: any, to: any): void => {
    for (let key in from) {
        if (from.hasOwnProperty(key)) {
            to[key] = from[key];
        }
    }
}


/**
 * 覆盖所有的字段到目标对象
 *
 * 只处理交集部分，复制完，复制完成之后，字段不会比之前多。
 *
 *  @param from - 原始对象
 * @param to - 目标对象
 * @param trim - 源对象有 undefined 的字段，是否将目标对应的字段置空
 */
const recover = (from: any, to: any, trim = true): void => {
    if (trim) {
        // 只复制对象已有的字段，如果源对象存在 undefined 字段，则置空对应字段
        for (let key in to) {
            if (to.hasOwnProperty(key)) {
                if (from.hasOwnProperty(key)) {
                    to[key] = from[key];
                } else {
                    to[key] = null;
                }
            }
        }
    } else {
        // 只复制交集部分的字段
        for (let key in to) {
            if (to.hasOwnProperty(key)) {
                if (from.hasOwnProperty(key)) {
                    to[key] = from[key];
                }
            }
        }
    }
}

/**
 * 返回一个空的函数
 *
 * @returns 空的函数
 */
const method = (): Function => empty_method;

/**
 * 构造一个空的函数，如果这个函数被调用，就会直接报错，用于强调必须重写的函数
 *
 * @param msg {string} 调用时的异常提示
 * @returns 返回一个空的函数，如果调用则会触发报错
 */
const abstractMethod = (msg): Function => {
    return () => {
        throw new MethodUndefinedException(msg);
    }
}

/**
 * 如果对象是函数则调用它
 *
 * @param call 可能是一个函数的对象
 * @param args 传递给函数的参数
 * @returns 函数返回值或undefined
 */
const executeFunction = <T extends unknown[], R>(call: unknown, ...args: T): R | undefined => {
    if (typeof call === 'function') {
        return (call as (...args: T) => R)(...args);
    }
    console.warn(`Expected a function but got: ${String(call)}`);
    return undefined;
};

/**
 * 是否存在于某个区间
 *
 *  区间表达式格式示例：
 *  - "(0,1)" 表示 0 < x < 1
 *  - "[0,1)" 表示 0 ≤ x < 1
 *  - "(-inf, 10]" 表示 x ≤ 10
 *  - "[5, inf)" 表示 x ≥ 5
 *
 * @param range - 数学区间表达式
 * @param val - 值
 * @returns 数值处在表单式表示的区间内
 */
const isInRange = (range: string, val: string | number): boolean => {
    // 去除所有空白符
    range = range.replace(/\s+/g, '').toLowerCase();

    val = Number(val);
    let idx = range.indexOf(',');
    if (idx) {
        throw new Error('illegal expression, which does not contain ', ' : ' + range);
    }

    // 解析上下区间
    const len = range.length;
    const left_bracket = range.charAt(0), right_bracket = range.charAt(len - 1);
    const num1 = range.substring(1, idx), num2 = range.substring(idx + 1, len - 1);


    // 下限判断
    if (num1 !== '-inf') {
        const n = Number(num1);
        if (isNaN(n)) {
            throw new Error('invalid left boundary of the interval: ' + range);
        }

        // 闭区间
        if (left_bracket === '[' && val < n) {
            return false;
        }

        // 开区间
        if (left_bracket === '(' && val <= n) {
            return false;
        }
    }

    // 上限判断
    if (num2 !== '-inf') {
        const n = Number(num2);
        if (isNaN(n)) {
            throw new Error('invalid right boundary of the interval: ' + range);
        }

        // 闭区间
        if (right_bracket === ']' && val > n) {
            return false;
        }

        // 开区间
        if (right_bracket === ')' && val >= n) {
            return false;
        }
    }
    return true;
}


/**
 * 链式调用一个函数，前一个结束，再调用下一个，
 *
 * 从 idx 开始，直到 len 结束，重复调用 (len - start) 次回调函数
 *
 * 区间：[idx, len)
 *
 * @param call  - 回调函数
 * @param idx - 起始数值
 * @param len - 终止数值
 * @returns {Promise<any>}
 */
const sequentialPromise = (call: ((resolve: Promise<any>, idx: number) => void), idx: number, len: number) => {
    let i = idx;

    function next() {
        if (i >= len) return Promise.resolve();
        return new Promise(resolve => {
            call(resolve, i);
            i++;
            return next().then(() => resolve()); // 保持链式特性
        });
    }

    return next();
}


/**
 * 去除代码中的注释
 * @param code
 */
const removeComments = (code) => {
    // 使用正则表达式去除单行注释
    code = code.replace(/\/\/.*$/gm, '');

    // 使用正则表达式去除多行注释
    // 注意：这个正则表达式假设多行注释不会嵌套
    code = code.replace(/\/\*[\s\S]*?\*\//g, '');

    return code;
}

/**
 * 基础函数
 *
 * 外部函数只会依赖于当前函数
 * ========================================================================
 */
export default {
    // factory method
    object, array

    // value check
    , isBlank, isNotBlank, isEmpty, isNotEmpty, hasText

    // nullable
    , nvl, ifNotBlank, ifNotEmpty

    // type case
    , toBoolean, toNumber, toString, toArray

    // copy method
    , clone, replace, recover

    // method
    , method, abstractMethod, executeFunction

    // others
    , isInRange, sequentialPromise

    , removeComments
}